﻿using AiBLSmartEdu.Module.SmartStockSense.API.Configurations;
using AiBLSmartEdu.Module.SmartStockSense.API.DTOs;
using FrameworkCore.DI;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Text.Json;

namespace AiBLSmartEdu.Module.SmartStockSense.API;

/// <summary>
/// 股票数据提供者
/// </summary>
public class StockDataProvider : ITransientDependency
{
    private readonly ILogger<StockDataProvider> _logger;
    private readonly IOptions<SmartStockSenseSettings> _optSmartStockSenseSettings;
    private readonly HttpClient _httpClient;
    private readonly StockLicenceProvider _stockLicenceProvider;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="logger">日志记录器</param>
    /// <param name="optSmartStockSenseSettings">配置信息</param>
    /// <param name="httpClient">HTTP请求</param>
    /// <param name="stockLicenceProvider">股票证书</param>
    public StockDataProvider(
        ILogger<StockDataProvider> logger,
        IOptions<SmartStockSenseSettings> optSmartStockSenseSettings,
        HttpClient httpClient,
        StockLicenceProvider stockLicenceProvider)
    {
        _logger = logger;
        _optSmartStockSenseSettings = optSmartStockSenseSettings;
        // 设置超时时间为120秒
        httpClient.Timeout = TimeSpan.FromSeconds(120);
        _httpClient = httpClient;
        _stockLicenceProvider = stockLicenceProvider;
    }

    /// <summary>
    /// 获取沪深两市股票列表
    /// </summary>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns></returns>
    public async Task<IEnumerable<StockDto>> GetStockListAsync(string? licence = default, CancellationToken cancellationToken = default)
    {
        string url = _optSmartStockSenseSettings.Value.ApiEndpoints.StockList;
        return await GetTechnicalIndicatorsAsync<StockDto>(url, string.Empty, default, licence, cancellationToken);
    }

    /// <summary>
    /// 获取历史分钟交易信息
    /// </summary>
    /// <param name="stockCode">股票代码</param>
    /// <param name="isHistorical">是否获取历史数据，默认获取历史数据</param>
    /// <param name="intervalMinute">间隔时间，默认为5分钟</param>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>返回KDJ技术指标的集合</returns>
    public async Task<IEnumerable<HistoricalMinuteTradingDto>> GetHistoricalMinuteTradingsAsync(
        string stockCode,
        bool isHistorical = true,
        string? intervalMinute = "5m",
        string? licence = default,
        CancellationToken cancellationToken = default)
    {
        // 根据是否获取历史数据选择合适的URL
        string url = isHistorical
            ? _optSmartStockSenseSettings.Value.ApiEndpoints.HistoricalMinuteTrading
            : _optSmartStockSenseSettings.Value.ApiEndpoints.LatestMinuteTrading;

        // 调用获取技术指标的方法并返回结果
        return await GetTechnicalIndicatorsAsync<HistoricalMinuteTradingDto>(url, stockCode, intervalMinute, licence, cancellationToken);
    }

    /// <summary>
    /// 获取KDJ指标值
    /// </summary>
    /// <param name="stockCode">股票代码</param>
    /// <param name="isHistorical">是否获取历史数据，默认获取历史数据</param>
    /// <param name="intervalMinute">间隔时间，默认为5分钟</param>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>返回KDJ技术指标的集合</returns>
    public async Task<IEnumerable<KDJTechnicalIndicatorDto>> GetKDJTechnicalIndicatorsAsync(
        string stockCode,
        bool isHistorical = true,
        string? intervalMinute = "5m",
        string? licence = default,
        CancellationToken cancellationToken = default)
    {
        // 根据是否获取历史数据选择合适的URL
        string url = isHistorical
            ? _optSmartStockSenseSettings.Value.ApiEndpoints.HistoricalKDJ
            : _optSmartStockSenseSettings.Value.ApiEndpoints.LatestKDJ;

        // 调用获取技术指标的方法并返回结果
        return await GetTechnicalIndicatorsAsync<KDJTechnicalIndicatorDto>(url, stockCode, intervalMinute, licence, cancellationToken);
    }

    /// <summary>
    /// 获取MACD指标值
    /// </summary>
    /// <param name="stockCode">股票代码</param>
    /// <param name="isHistorical">是否获取历史数据</param>
    /// <param name="intervalMinute">间隔时间，默认5分钟</param>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>返回MACD技术指标的集合</returns>
    public async Task<IEnumerable<MACDTechnicalIndicatorDto>> GetMACDTechnicalIndicatorsAsync(
        string stockCode,
        bool isHistorical = true, // 新增的参数，默认获取历史数据
        string? intervalMinute = "5m",
        string? licence = default,
        CancellationToken cancellationToken = default)
    {
        string url = isHistorical
            ? _optSmartStockSenseSettings.Value.ApiEndpoints.HistoricalMACD
            : _optSmartStockSenseSettings.Value.ApiEndpoints.LatestMACD;

        return await GetTechnicalIndicatorsAsync<MACDTechnicalIndicatorDto>(url, stockCode, intervalMinute, licence, cancellationToken);
    }

    /// <summary>
    /// 获取MA指标值
    /// </summary>
    /// <param name="stockCode">股票代码</param>
    /// <param name="isHistorical">是否获取历史数据</param>
    /// <param name="intervalMinute">间隔时间，默认5分钟</param>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>返回MA技术指标的集合</returns>
    public async Task<IEnumerable<MATechnicalIndicatorDto>> GetMATechnicalIndicatorsAsync(
        string stockCode,
        bool isHistorical = true, // 新增的参数，默认获取历史数据
        string? intervalMinute = "5m",
        string? licence = default,
        CancellationToken cancellationToken = default)
    {
        string url = isHistorical
            ? _optSmartStockSenseSettings.Value.ApiEndpoints.HistoricalMA
            : _optSmartStockSenseSettings.Value.ApiEndpoints.LatestMA;

        return await GetTechnicalIndicatorsAsync<MATechnicalIndicatorDto>(url, stockCode, intervalMinute, licence, cancellationToken);
    }

    /// <summary>
    /// 获取Boll指标值
    /// </summary>
    /// <param name="stockCode">股票代码</param>
    /// <param name="intervalMinute">间隔时间，默认5分钟</param>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>返回Boll技术指标的集合</returns>
    public async Task<IEnumerable<BollTechnicalIndicatorDto>> GetBollTechnicalIndicatorsAsync(
        string stockCode,
        string? intervalMinute = "5m",
        string? licence = default,
        CancellationToken cancellationToken = default)
    {
        string url = _optSmartStockSenseSettings.Value.ApiEndpoints.HistoricalBOLL;
        return await GetTechnicalIndicatorsAsync<BollTechnicalIndicatorDto>(url, stockCode, intervalMinute, licence, cancellationToken);
    }

    /// <summary>
    /// 获取FiveLevelMarket指标值
    /// </summary>
    /// <param name="stockCode">股票代码</param>
    /// <param name="isHistorical">是否获取历史数据，默认获取历史数据</param>
    /// <param name="intervalMinute">间隔时间，默认为5分钟</param>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>返回KDJ技术指标的集合</returns>
    public async Task<IEnumerable<FiveLevelMarketTechnicalIndicatorDto>> GetFiveLevelMarketTechnicalIndicatorsAsync(
        string stockCode,
        bool isHistorical = true,
        string? intervalMinute = "5m",
        string? licence = default,
        CancellationToken cancellationToken = default)
    {
        // 根据是否获取历史数据选择合适的URL
        string url = isHistorical
            ? _optSmartStockSenseSettings.Value.ApiEndpoints.FiveLevelMarket
            : _optSmartStockSenseSettings.Value.ApiEndpoints.FiveLevelMarket;

        // 调用获取技术指标的方法并返回结果
        return await GetTechnicalIndicatorsAsync<FiveLevelMarketTechnicalIndicatorDto>(url, stockCode, intervalMinute, licence, cancellationToken);
    }

    /// <summary>
    /// 获取指标值
    /// </summary>
    /// <param name="url">接口链接</param>
    /// <param name="stockCode">股票代码</param>
    /// <param name="intervalMinute">间隔时间默认5分钟</param>
    /// <param name="licence">请求证书</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns></returns>
    public async Task<IEnumerable<TDto>> GetTechnicalIndicatorsAsync<TDto>(
        string url,
        string stockCode,
        string? intervalMinute = "5m",
        string? licence = null,
        CancellationToken cancellationToken = default) where TDto : class
    {
        IEnumerable<TDto> result = new List<TDto>();
        var isDefaultLicence = string.IsNullOrWhiteSpace(licence);
        var disable = false;
        var attemptCount = 0;

        while (true)
        {
            if (isDefaultLicence)
            {
                (licence, disable) = _stockLicenceProvider.GetLicence();
                if (disable)
                {
                    _logger.LogError($"{nameof(StockDataProvider)}: 请求失败，方法: {nameof(GetTechnicalIndicatorsAsync)}, 错误信息: 无可用的证书");
                    break;
                }
            }

            try
            {
                return await FetchDataAsync<TDto>(url, stockCode, intervalMinute, licence!, cancellationToken);
            }
            catch (HttpRequestException e)
            {
                if (e.Message.Contains("Too Many Requests"))
                {
                    _logger.LogWarning("请求过于频繁，正在重试... 第{Attempt}次", attemptCount + 1);
                    _stockLicenceProvider.DisableLicence(licence);
                    licence = null; // 清空已使用的许可证以获取新的
                    await Task.Delay(1000); // 等待一段时间后再重试
                }
                else
                {
                    _logger.LogError(e, "{StockDataProvider}: 请求失败，方法: {Method}, 错误信息: {Message}", nameof(StockDataProvider), nameof(GetTechnicalIndicatorsAsync), e.Message);
                    break;
                }
            }
        }

        return result;
    }


    private async Task<IEnumerable<TDto>> FetchDataAsync<TDto>(
        string url,
        string stockCode,
        string? intervalMinute,
        string licence,
        CancellationToken cancellationToken,
        int maxRetries = 3, 
        int delayMilliseconds = 2000)  
        where TDto : class
    {
        // 格式化 URL
        url = string.Format(url, licence, stockCode, intervalMinute);

        // 创建请求消息
        using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);
        //requestMessage.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
        //requestMessage.Headers.Add("Accept", "application/json, text/javascript, */*; q=0.01");
        //requestMessage.Headers.Add("Accept-Language", "en-US,en;q=0.9");
        //requestMessage.Headers.Add("Connection", "keep-alive");

        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                // 发送请求
                HttpResponseMessage response = await _httpClient.SendAsync(requestMessage, cancellationToken);
                response.EnsureSuccessStatusCode();

                // 读取响应内容
                string result = await response.Content.ReadAsStringAsync(cancellationToken);

                if (string.IsNullOrWhiteSpace(result))
                {
                    return new List<TDto>();
                }

                // 尝试反序列化 JSON 数据
                try
                {
                    // 如果结果是一个数组
                    var stockList = JsonSerializer.Deserialize<List<TDto>>(result);
                    return stockList ?? new List<TDto>();
                }
                catch (JsonException)
                {
                    // 如果结果是单个对象而不是数组，尝试将其作为单个对象处理
                    var singleItem = JsonSerializer.Deserialize<TDto>(result);
                    return singleItem != null ? new List<TDto> { singleItem } : new List<TDto>();
                }
            }
            catch (HttpRequestException ex) when (attempt < maxRetries - 1)
            {
                // 捕获请求异常，准备重试
                Console.WriteLine($"请求失败: {ex.Message}. 正在重试... (第 {attempt + 1} 次)");
                await Task.Delay(delayMilliseconds); // 延迟后重试
            }
            catch (OperationCanceledException) when (attempt < maxRetries - 1
                                                         && !cancellationToken.IsCancellationRequested)
            {
                // 如果请求被取消，但未超过最大重试次数，则重试
                Console.WriteLine($"请求被取消。正在重试... (第 {attempt + 1} 次)");
                await Task.Delay(delayMilliseconds); // 延迟后重试
            }
        }

        // 超过最大重试次数后抛出异常
        throw new Exception("请求失败，已达到最大重试次数.");
    }
}
